﻿//////////////////////////////////////////////////////////////////////////////
//
//  Copyright © 1998-2024 Glodon Company Limited.  All rights reserved.
//
//  Use of this software is subject to the terms of the Glodon license
//  agreement provided at the time of installation or download, or which
//  otherwise accompanies this software in either electronic or hard copy form.  
//
//////////////////////////////////////////////////////////////////////////////


#pragma once
#include "GnufBase.h"
#include "GnufUiCommonDef.h"

namespace gnuf {

    class GNUF_BASE_EXPORT Color final
    {
    public:
        Color() {}
        Color(const Color & that) : r(that.r), g(that.g), b(that.b), a(that.a) {}
        Color(const char * html);
        Color(std::uint8_t _r, std::uint8_t _g, std::uint8_t _b) : r(_r), g(_g), b(_b), a(255) {}
        constexpr Color(std::uint8_t _r, std::uint8_t _g, std::uint8_t _b, std::uint8_t _a) : r(_r), g(_g), b(_b), a(_a) {}

        std::string hex(bool plusAlpha = true) const;
        std::string rgb() const;
        std::string rgba() const;
        std::uint8_t r = 0, g = 0, b = 0, a = 255;

        bool operator==(const Color & that) const {
            return (
                this->r == that.r &&
                this->g == that.g &&
                this->b == that.b &&
                this->a == that.a);
        }

        bool operator!=(const Color & that) const {
            return (
                this->r != that.r ||
                this->g != that.g ||
                this->b != that.b ||
                this->a != that.a);
        }

        Color & operator=(const Color & that) {
            this->r = that.r;
            this->g = that.g;
            this->b = that.b;
            this->a = that.a;
            return *this;
        }

        Color& setAlpha(std::uint8_t alpha) { a = alpha; return *this; }
        Color& setAlphaF(float alpha) {
            if (alpha < 0.0)
                alpha = 0.0;
            else if (alpha > 1.0)
                alpha = 1.0;
            a = static_cast<std::uint8_t>(alpha * 255);
            return *this;
        }
        Color& setRed(std::uint8_t red) { r = red; return *this;}
        Color& setGreen(std::uint8_t green) { g = green; return *this;}
        Color& setBlue(std::uint8_t blue) { b = blue; return *this; }

        static const Color& fromCode(ColorCode key, bool disabled = false);
    };

    class GNUF_BASE_EXPORT Icon final
    {
    public:
        Icon();
        Icon(const std::wstring & dir);
        Icon(const Icon &);

        void setDir(const std::wstring & dir);
        const std::wstring & dir() const { return m_dir; }

        bool operator==(const Icon & that) const {
            // Only take care of the m_dir. The m_path is temporary value.
            return (0 == this->m_dir.compare(that.m_dir));
        }
        Icon & operator=(const Icon & that) {
            this->m_dir = that.m_dir;
            return *this;
        }
        bool operator==(const std::wstring & that) const {
            // Only take care of the m_dir. The m_path is temporary value.
            return (0 == this->m_dir.compare(that));
        }
        Icon & operator=(const std::wstring & that) {
            this->m_dir = that;
            return *this;
        }

        // Return the icon path by size and status.
        const std::wstring & get(
            const EIconSize,
            const EIconStatus = EIconStatus::eDefault,
            const bool checkIfExisted = true) const;

        // Return the icon path by name and status.
        const std::wstring & get(
            const std::wstring & name,
            const EIconStatus status = EIconStatus::eDefault,
            const bool checkIfExisted = true) const;

        const std::wstring & getOriginIcon(
            const std::uint16_t width,
            const std::uint16_t height,
            const EIconStatus status = EIconStatus::eDefault,
            const bool checkIfExisted = true) const;

        // Return the icon path by size(width & height) and status.
        const std::wstring & get(
            const std::uint16_t width,
            const std::uint16_t height,
            const EIconStatus status = EIconStatus::eDefault,
            const bool checkIfExisted = true) const;

        // EIconStatus::eDefault //////////////////////////////////////////////
        inline const std::wstring & get4x4(const bool checkIfExisted = true) const {
            return this->get(EIconSize::e4x4, EIconStatus::eDefault, checkIfExisted);
        }
        inline const std::wstring & get8x8(const bool checkIfExisted = true) const {
            return this->get(EIconSize::e8x8, EIconStatus::eDefault, checkIfExisted);
        }
        inline const std::wstring & get16x16(const bool checkIfExisted = true) const {
            return this->get(EIconSize::e16x16, EIconStatus::eDefault, checkIfExisted);
        }
        inline const std::wstring& get12x12(const bool checkIfExisted = true) const {
            return this->get(EIconSize::e12x12, EIconStatus::eDefault, checkIfExisted);
        }
        inline const std::wstring & get24x24(const bool checkIfExisted = true) const {
            return this->get(EIconSize::e24x24, EIconStatus::eDefault, checkIfExisted);
        }
        inline const std::wstring & get32x32(const bool checkIfExisted = true) const {
            return this->get(EIconSize::e32x32, EIconStatus::eDefault, checkIfExisted);
        }
        inline const std::wstring & get48x48(const bool checkIfExisted = true) const {
            return this->get(EIconSize::e48x48, EIconStatus::eDefault, checkIfExisted);
        }
        inline const std::wstring & get64x64(const bool checkIfExisted = true) const {
            return this->get(EIconSize::e64x64, EIconStatus::eDefault, checkIfExisted);
        }
        inline const std::wstring & get96x96(const bool checkIfExisted = true) const {
            return this->get(EIconSize::e96x96, EIconStatus::eDefault, checkIfExisted);
        }
        inline const std::wstring& get128x128(const bool checkIfExisted = true) const {
            return this->get(EIconSize::e128x128, EIconStatus::eDefault, checkIfExisted);
        }
        inline const std::wstring& get256x256(const bool checkIfExisted = true) const {
            return this->get(EIconSize::e256x256, EIconStatus::eDefault, checkIfExisted);
        }
        // EIconStatus::eDisabled /////////////////////////////////////////////
        inline const std::wstring & get4x4Disabled(const bool checkIfExisted = true) const {
            return this->get(EIconSize::e4x4, EIconStatus::eDisabled, checkIfExisted);
        }
        inline const std::wstring & get8x8Disabled(const bool checkIfExisted = true) const {
            return this->get(EIconSize::e8x8, EIconStatus::eDisabled, checkIfExisted);
        }
        inline const std::wstring& get12x12Disabled(const bool checkIfExisted = true) const {
            return this->get(EIconSize::e12x12, EIconStatus::eDisabled, checkIfExisted);
        }
        inline const std::wstring & get16x16Disabled(const bool checkIfExisted = true) const {
            return this->get(EIconSize::e16x16, EIconStatus::eDisabled, checkIfExisted);
        }
        inline const std::wstring & get24x24Disabled(const bool checkIfExisted = true) const {
            return this->get(EIconSize::e24x24, EIconStatus::eDisabled, checkIfExisted);
        }
        inline const std::wstring & get32x32Disabled(const bool checkIfExisted = true) const {
            return this->get(EIconSize::e32x32, EIconStatus::eDisabled, checkIfExisted);
        }
        inline const std::wstring & get48x48Disabled(const bool checkIfExisted = true) const {
            return this->get(EIconSize::e48x48, EIconStatus::eDisabled, checkIfExisted);
        }
        inline const std::wstring & get64x64Disabled(const bool checkIfExisted = true) const {
            return this->get(EIconSize::e64x64, EIconStatus::eDisabled, checkIfExisted);
        }
        // EIconStatus::eHovered //////////////////////////////////////////////
        inline const std::wstring & get4x4Hovered(const bool checkIfExisted = true) const {
            return this->get(EIconSize::e4x4, EIconStatus::eHovered, checkIfExisted);
        }
        inline const std::wstring & get8x8Hovered(const bool checkIfExisted = true) const {
            return this->get(EIconSize::e8x8, EIconStatus::eHovered, checkIfExisted);
        }
        inline const std::wstring& get12x12Hovered(const bool checkIfExisted = true) const {
            return this->get(EIconSize::e12x12, EIconStatus::eHovered, checkIfExisted);
        }
        inline const std::wstring & get16x16Hovered(const bool checkIfExisted = true) const {
            return this->get(EIconSize::e16x16, EIconStatus::eHovered, checkIfExisted);
        }
        inline const std::wstring & get24x24Hovered(const bool checkIfExisted = true) const {
            return this->get(EIconSize::e24x24, EIconStatus::eHovered, checkIfExisted);
        }
        inline const std::wstring & get32x32Hovered(const bool checkIfExisted = true) const {
            return this->get(EIconSize::e32x32, EIconStatus::eHovered, checkIfExisted);
        }
        inline const std::wstring & get48x48Hovered(const bool checkIfExisted = true) const {
            return this->get(EIconSize::e48x48, EIconStatus::eHovered, checkIfExisted);
        }
        inline const std::wstring & get64x64Hovered(const bool checkIfExisted = true) const {
            return this->get(EIconSize::e64x64, EIconStatus::eHovered, checkIfExisted);
        }
        // EIconStatus::eSelected /////////////////////////////////////////////
        inline const std::wstring & get4x4Selected(const bool checkIfExisted = true) const {
            return this->get(EIconSize::e4x4, EIconStatus::eSelected, checkIfExisted);
        }
        inline const std::wstring & get8x8Selected(const bool checkIfExisted = true) const {
            return this->get(EIconSize::e8x8, EIconStatus::eSelected, checkIfExisted);
        }
        inline const std::wstring& get12x12Selected(const bool checkIfExisted = true) const {
            return this->get(EIconSize::e12x12, EIconStatus::eSelected, checkIfExisted);
        }
        inline const std::wstring & get16x16Selected(const bool checkIfExisted = true) const {
            return this->get(EIconSize::e16x16, EIconStatus::eSelected, checkIfExisted);
        }
        inline const std::wstring & get24x24Selected(const bool checkIfExisted = true) const {
            return this->get(EIconSize::e24x24, EIconStatus::eSelected, checkIfExisted);
        }
        inline const std::wstring & get32x32Selected(const bool checkIfExisted = true) const {
            return this->get(EIconSize::e32x32, EIconStatus::eSelected, checkIfExisted);
        }
        inline const std::wstring & get48x48Selected(const bool checkIfExisted = true) const {
            return this->get(EIconSize::e48x48, EIconStatus::eSelected, checkIfExisted);
        }
        inline const std::wstring & get64x64Selected(const bool checkIfExisted = true) const {
            return this->get(EIconSize::e64x64, EIconStatus::eSelected, checkIfExisted);
        }

        // Check if the file exists under the directory.
        bool doesExist(const std::wstring & fileName) const;

    private:
        const std::wstring & emptyPath() const;

        const std::wstring & getByName(
            const std::wstring & name,
            const EIconStatus status,
            const bool checkIfExisted = true) const;

        const std::wstring & getBySize(
            const std::uint16_t width,
            const std::uint16_t height,
            const EIconStatus status,
            const bool checkIfExisted = true) const;

        const std::wstring & compose(
            const std::wstring & name,
            const std::uint16_t width,
            const std::uint16_t height,
            const EIconStatus status,
            const bool checkIfExisted,
            const bool showWarningIfNotExisted) const;

        mutable std::wstring m_dir;
        mutable std::wstring m_path;
    };

    class GNUF_BASE_EXPORT Point
    {
    public:
        int x, y;
        Point();
        Point(int x, int y);

        bool operator<(const Point& that) const
        {
            return this->y < that.y || (this->y == that.y && this->x < that.x);
        }

        bool operator==(const Point& that) const 
        {
            return this->x == that.x && this->y == that.y;
        }
    };
    class GNUF_BASE_EXPORT Size
    {
    public:
        int width, height;
        Size();
        Size(int width, int height);
        bool isValid() const { return (this->width > 0 && this->height > 0); }
    };
    class GNUF_BASE_EXPORT Rectangle
    {
    public:
        gnuf::Point pos;
        gnuf::Size size;
        Rectangle();
        Rectangle(int x, int y, int witdh, int height);
    };

    template <class TValue>
    class GNUF_BASE_EXPORT Range
    {
    public:
        TValue minimum, maximum, value;
        Range() : minimum(0), maximum(0), value(0) {}
        Range(TValue mi, TValue ma, TValue va)
            : minimum(mi), maximum(ma), value(va) {}
        bool isValid() const {
            return (
                this->maximum > this->minimum &&
                this->inRange(this->value));
        }
        bool inRange(const int val) const {
            return (val < this->minimum || this->maximum < val) ? false : true;
        }
        TValue span() const { return (this->maximum - this->minimum); }
        float percent() const {
            if (!isValid())
                return 0.0;

            return (
                static_cast<float>(this->value) /
                static_cast<float>(this->span()));
        }

        bool operator ==(const Range<TValue>& that) const
        {
            return (
                this->minimum == that.minimum &&
                this->maximum == that.maximum &&
                this->value == that.value);
        }
        
        bool operator !=(const Range<TValue>& that) const
        {
            return (
                this->minimum != that.minimum ||
                this->maximum != that.maximum ||
                this->value != that.value);
        }
        
        template<class TValue>
        Range<TValue>& operator =(const Range<TValue>& that) {
            this->minimum = that.minimum;
            this->maximum = that.maximum;
            this->value = that.value;
            return *this;
        }
    };
    template<>
    bool Range<float>::operator ==(const Range<float>& that) const
    {
        return (
            std::abs(this->minimum - that.minimum) < 1e-4 &&
            std::abs(this->maximum - that.maximum) < 1e-4 &&
            std::abs(this->value - that.value) < 1e-4);
    }
    template<>
    bool Range<double>::operator ==(const Range<double>& that) const
    {
        return (
            std::abs(this->minimum - that.minimum) < 1e-8 &&
            std::abs(this->maximum - that.maximum) < 1e-8 &&
            std::abs(this->value - that.value) < 1e-8);
    }
    template<>
    bool Range<float>::operator !=(const Range<float>& that) const
    {
        return (
            std::abs(this->minimum - that.minimum) >= 1e-4 ||
            std::abs(this->maximum - that.maximum) >= 1e-4 ||
            std::abs(this->value - that.value) >= 1e-4);
    }
    template<>
    bool Range<double>::operator !=(const Range<double>& that) const
    {
        return (
            std::abs(this->minimum - that.minimum) >= 1e-8 ||
            std::abs(this->maximum - that.maximum) >= 1e-8 ||
            std::abs(this->value - that.value) >= 1e-8);
    }

    class GNUF_BASE_EXPORT Tooltip
    {
    public:
        explicit Tooltip();
        explicit Tooltip(const Tooltip &);
        explicit Tooltip(
            const std::wstring & caption,
            const std::wstring & description = L"",
            const std::wstring & demonstration = L"",
            const std::wstring & explanation = L"");
        ~Tooltip();

        void clear();

        bool empty() const {
            return (m_caption.empty() && m_description.empty() && m_demonstration.empty() && m_explanation.empty());
        }
        bool simple() const {
            return (!m_caption.empty() && !complex());
        }
        bool complex() const {
            return (!m_description.empty() || !m_demonstration.empty() || !m_explanation.empty());
        }

        // operator
        bool operator==(const Tooltip &) const;
        Tooltip & operator=(const Tooltip &);

        const std::wstring & caption() const { return m_caption; }
        void setCaption(const std::wstring & val) { m_caption = val; }
        
        // Append a hotkey at the end of the caption. If existed, replace it with
        // the new hotkey. Return the new caption that contained the new hotkey.
        const std::wstring & appendHotkeyToCaption(const std::wstring &);
        
        // Return the hotkey.
        std::wstring hotkey() const;

        const std::wstring & description() const { return m_description; }
        void setDescription(const std::wstring & val) { m_description = val; }

        const std::wstring & demonstration() const { return m_demonstration; }
        void setDemonstration(const std::wstring & val) { m_demonstration = val; }

        const std::wstring& explanation() const { return m_explanation; }
        void setExplanation(const std::wstring& val) { m_explanation = val; }

        void set(
            const std::wstring & caption,
            const std::wstring & description,
            const std::wstring & demonstration,
            const std::wstring & explanation = L"")
        {
            m_caption = caption;
            m_description = description;
            m_demonstration = demonstration;
            m_explanation = explanation;
        }

    private:
        bool separateCaption(std::wstring * originalCaption, std::wstring * hotkey) const;
        std::wstring m_caption, m_description, m_demonstration, m_explanation;
    };

    class GNUF_BASE_EXPORT BaseUtil
    {
    public:
        // Theme...
        static const std::wstring s_themeUnknown;
        static const std::wstring s_themeLight;
        static const std::wstring s_themeDark;
        static ETheme currentTheme();
        static ETheme toTheme(const std::wstring &);
        static const std::wstring & toThemeName(ETheme);

        static ColorCode toColorCode(const std::wstring&);
        static std::wstring toColorKey(ColorCode);

        // Set/Get the background color to the theme.
        static void setBackgroundColor(ETheme, const Color &);
        static const Color * backgroundColorOf(ETheme);

        // Process all events that are in the main event loop of
        // the Qt application, if it is not playing a journal.
        static void processQtEvent();

        template <typename TOwnerPtrs, typename TObject = TOwnerPtrs::value_type::Type>
        static std::vector< TObject * > allOfOwned(TOwnerPtrs & ownerPtrs)
        {
            std::vector< TObject * > ptrs( ownerPtrs.size(), nullptr ); // initialize with nullptr

            size_t index = 0;
            for (auto & opObject : ownerPtrs)
            {
                TObject * pObject = opObject.get();
                DBG_WARN_AND_RETURN_UNLESS(nullptr != pObject, (0 == index ?
                    std::vector< TObject * >() :                                    // just return a empty vector
                    std::vector< TObject * >(ptrs.begin(), ptrs.begin() + index)),  // only return those valid
                    L"pObject is nullptr! Fix it, soon!", L"fangkunfk", L"2022-08-01");

                ptrs[ index ] = pObject;

                ++index; // next
            }

            return ptrs;
        }

        template <typename TOwnerPtrs, typename TObject = TOwnerPtrs::value_type::Type>
        static std::list< TObject * > listOfOwned(TOwnerPtrs & ownerPtrs)
        {
            std::list< TObject * > ptrs;

            for (auto & opObject : ownerPtrs)
            {
                TObject * pObject = opObject.get();
                DBG_WARN_AND_RETURN_UNLESS(nullptr != pObject, ptrs,                // only return those valid
                    L"pObject is nullptr! Fix it, soon!", L"fangkunfk", L"2022-08-01");

                ptrs.emplace_back( pObject );
            }

            return ptrs;
        }
        
        template <typename TWeakPtrs, typename TObject = TWeakPtrs::value_type::Type>
        static std::vector< TObject * > allOfWeakRefed(TWeakPtrs & weakPtrs)
        {
            std::vector< TObject * > ptrs;
            ptrs.reserve(weakPtrs.size());

            size_t index = 0;
            for (auto & wpObject : weakPtrs)
            {
                if (wpObject.IsValid())
                    ptrs.emplace_back(wpObject.Get());
                
                ++index; // next
            }

            return ptrs;
        }

        template <typename TOnes, typename TIsInvalid>
        static size_t wringOutInvalid(TOnes & ones, const TIsInvalid & isInvalid)
        {
            // Remove logically.
            auto removed = std::remove_if(ones.begin(), ones.end(),
                [ &isInvalid ](const TOnes::value_type & one)->bool
            {
                return isInvalid( one );
            });

            // And then, erase physically.
            const size_t count = std::distance(removed, ones.end());
            ones.erase(removed, ones.end());

            return count;
        }
    };

    class GNUF_BASE_EXPORT String
    {
    public:
        static std::wstring toHtmlImg(const std::wstring & srcPath);
    };

    class GNUF_BASE_EXPORT ScaleFactor
    {
    public:
        explicit ScaleFactor();
        explicit ScaleFactor(double value);
        explicit ScaleFactor(unsigned short dpi);
                 ScaleFactor(const ScaleFactor &);
        static const ScaleFactor & defaultOne();
        ScaleFactor & operator=(const ScaleFactor &);
        template<class TValue>
        TValue operator*(TValue value) const
        {
            return static_cast<TValue>(static_cast<double>(value) * asDouble());
        }
        bool operator==(const ScaleFactor &) const;
        double asDouble()       const { return m_value; }
        int    asInt()          const { return static_cast<int>(m_value); }
        double asRoundDouble()  const { return std::round(m_value); }
        int    asRoundInt()     const { return static_cast<int>(asRoundDouble()); }
    private:
        double m_value;
    };
    template<class TValue>
    static TValue operator/(TValue value, const ScaleFactor & scaleFactor)
    {
        return static_cast<TValue>(static_cast<double>(value) / scaleFactor.asDouble());
    }
    template<class TValue>
    static void operator/=(TValue & value, const ScaleFactor & scaleFactor)
    {
        value = static_cast<TValue>(static_cast<double>(value) / scaleFactor.asDouble());
    }
    template<class TValue>
    static TValue operator*(TValue value, const ScaleFactor & scaleFactor)
    {
        return static_cast<TValue>(static_cast<double>(value) * scaleFactor.asDouble());
    }
    template<class TValue>
    static void operator*=(TValue & value, const ScaleFactor & scaleFactor)
    {
        value = static_cast<TValue>(static_cast<double>(value) * scaleFactor.asDouble());
    }
}
